/*-------------------------------------------------------------------------
 *
 * subscripting.h
 *		通用类型下标的 API
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/subscripting.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SUBSCRIPTING_H
#define SUBSCRIPTING_H

#include "nodes/primnodes.h"

/* 前置声明，以避免包含其他头文件 */
struct ParseState;
struct SubscriptingRefState;
struct SubscriptExecSteps;

/*
 * 定义子脚本方法的 SQL 可见函数被声明为
 *		subscripting_function(internal) returns internal
 * 但实际上并没有传递任何参数。它必须返回一个指向"结构 SubscriptRoutines"的指针，该结构提供指向各个子脚本解析和执行方法的指针。通常，该指针将指向一个"static const"变量，但如有需要，它可以指向 palloc 的空间。
 * 子脚本构造的头变量或表达式的类型（在域扁平化后）决定了为该构造调用哪个子脚本函数。
 *
 * 除了方法指针，结构 SubscriptRoutines 包括几个布尔标志，指定该数据类型可以执行的子脚本操作的属性：
 *
 * fetch_strict 表示 fetch SubscriptRef 是严格的，即如果任何输入（容器或任何子脚本）为 NULL，则返回 NULL。
 *
 * fetch_leakproof 表示 fetch SubscriptRef 是无泄漏的，即不会抛出任何依赖于数据值的错误。通常，这要求对无效的子脚本静默返回 NULL。
 *
 * store_leakproof 类似地表示赋值 SubscriptRef 是否是无泄漏的。（在赋值中，通常更倾向于针对无效子脚本抛出错误；这没问题，但会使操作不是无泄漏的。在当前使用中，使赋值无泄漏没有优势。）
 *
 * 没有 store_strict 标志。这种行为通常是不可取的，因为例如，在赋值中，一个 null 子脚本将导致整个容器变为 NULL。
 *
 * 无论这些标志如何，所有 SubscriptRefs 预计是不可变的，即它们对于相同的输入必须始终给出相同的结果。它们还预计始终是并行安全的。
 */


/*
 * transform 方法在解析分析一个下标构造时被调用。SubscriptingRef 节点已被构建，但其某些字段仍需填充，而下标表达式仍处于原始形式。transform 方法负责对每个下标表达式进行解析分析（使用 transformExpr），将下标强制转换为它所需的任何类型，并从这些结果构建 refupperindexpr 和 reflowerindexpr 列表。对于元素操作，reflowerindexpr 列表必须为空，或者对于切片操作，其长度必须与 refupperindexpr 相同。在切片操作中，对于任何省略的下标，插入 NULL（即一个空的解析树，而不是一个空的 Const 节点）。当然，如果 transform 方法不想支持切片，则可以在 isSlice 为真时直接抛出错误。有关示例代码，请参见 array_subscript_transform()。
 *
 * transform 方法还负责识别下标操作的结果类型。调用时，refcontainertype 和 reftypmod 描述容器类型（这将是基类型而不是域），而 refelemtype 被设置为容器类型的 pg_type.typelem 值。transform 方法必须设置 refrestype 和 reftypmod 以描述下标的结果。对于数组，refrestype 在元素操作时设置为 refelemtype，而在切片时设置为 refcontainertype，reftypmod 在两种情况下保持不变；但其他类型可能使用其他规则。transform 方法应忽略 refcollid，因为这是在解析过程中稍后确定的。
 *
 * 在调用时，refassgnexpr 尚未填充，因此 SubscriptingRef 节点总是看起来像是一个提取；refrestype 也应设置得像是用于提取的那样。（isAssignment 参数通常仅对 transform 方法想要因为不支持赋值而抛出错误时有用。）为完成赋值的处理，核心解析器将在将元素/切片源表达式放入 refassgnexpr 之前，将其强制转换为返回的 refrestype 和 reftypmod。然后，它将再次设置 refrestype 和 reftypmod 以描述容器类型，因为赋值必须返回这个类型。
 */  
typedef void (*SubscriptTransform) (SubscriptingRef *sbsref,
									List *indirection,
									struct ParseState *pstate,
									bool isSlice,
									bool isAssignment);


/*
 * exec_setup 方法在表达式中的 SubscriptingRef 节点的执行器启动编译期间被调用。它必须填充 *methods 以指向可用于执行该节点的函数。可选地，exec_setup 可以初始化 sbsrefstate->workspace，以指向某个为执行而分配的工作区。（通常，这种工作区用于保存查找的目录数据和/或为 check_subscripts 步骤提供空间，以将数据传递给其他步骤函数。）有关这些结构以及其他在表达式执行中使用的结构的定义，请参见 executor/execExpr.h。
 *
 * 需要提供的方法是：
 *
 * sbs_check_subscripts：检查 sbsrefstate 的数组中可用的刚计算出的下标值，并可能将它们转换为另一种形式（存储在 sbsrefstate->workspace 中）。返回 TRUE 以继续评估下标构造，或 FALSE 以跳过它并返回整体 NULL 结果。如果这是一个获取操作，并且数据类型的 fetch_strict 标志为 true，则如果存在任何 NULL 下标，sbs_check_subscripts 必须返回 FALSE。否则，它可以选择抛出错误，或返回 FALSE，或让 sbs_fetch 或 sbs_assign 处理空下标。
 *
 * sbs_fetch：执行下标获取，使用 *op->resvalue 中的容器值和来自 sbs_check_subscripts 的下标。如果 fetch_strict 为 true，则可以假定所有这些输入都非 NULL，否则 sbs_fetch 必须检查空输入。在 *op->resvalue / *op->resnull 中放置结果。
 *
 * sbs_assign：执行下标赋值，使用 *op->resvalue / *op->resnull 中的原始容器值、来自 sbs_check_subscripts 的下标以及 sbsrefstate->replacevalue/replacenull 中的新元素/切片值。这些输入中的任何一个可能为 NULL（除非 sbs_check_subscripts 拒绝空下标）。将结果（一个全新的容器值）放入 *op->resvalue / *op->resnull 中。
 *
 * sbs_fetch_old：仅在元素或切片赋值涉及对子字段或子元素的赋值时使用（即，涉及嵌套容器）。它必须获取目标元素或切片的现有值。这与 sbs_fetch 完全相同，除了 (a) 必须处理 NULL 容器，以及 sbs_check_subscripts 允许 NULL 下标的情况（通常，返回 NULL 已足够）；以及 (b) 结果必须放入 sbsrefstate->prevvalue/prevnull，而不覆盖 *op->resvalue。
 *
 * 不支持赋值的下标实现无需提供 sbs_assign 或 sbs_fetch_old 方法。省略 sbs_check_subscripts 也是合理的，在这种情况下，sbs_fetch 方法必须结合 sbs_check_subscripts 和 sbs_fetch 的功能。（存在单独的 sbs_check_subscripts 方法的主要原因是 sbs_fetch_old 和 sbs_assign 无需重复下标处理。）将任何省略的方法的相关指针设置为 NULL。
 */
typedef void (*SubscriptExecSetup) (const SubscriptingRef *sbsref,
									struct SubscriptingRefState *sbsrefstate,
									struct SubscriptExecSteps *methods);

/* 由 SQL 可见的下标处理函数返回的结构 */
typedef struct SubscriptRoutines
{
	SubscriptTransform transform;	/* 解析分析函数 */
	SubscriptExecSetup exec_setup;	/* 表达式编译函数 */
	bool		fetch_strict;	/* 获取 SubscriptRef 是否严格? */
	bool		fetch_leakproof;	/* 获取 SubscriptRef 是否防泄漏? */
	bool		store_leakproof;	/* 赋值 SubscriptRef 是否防泄漏? */
} SubscriptRoutines;

#endif							/* SUBSCRIPTING_H */
